﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;

using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;

using zijian666.Core;
using zijian666.WebApi.Attributes;

namespace zijian666.WebApi
{
    public partial class WebApiOptions
    {
        private readonly List<Type> _dynamicControllerTypes = new();

        /// <summary>
        /// 将指定接口添加为控制器
        /// </summary>
        public void AddInterfacesAsControllers(params Type[] interfaceTypes)
            => AddInterfacesAsControllers((IEnumerable<Type>)interfaceTypes);

        /// <summary>
        /// 将标记为 <seealso cref="HttpApiAttribute"/> 的接口添加为控制器
        /// </summary>
        public void AddInterfacesAsControllers()
            => AddInterfacesAsControllers(AssemblyResolver.Default.GetTypesWithAttribute<HttpApiAttribute>().Select(x => x.Item1));

        /// <summary>
        /// 将命名空间为 <paramref name="namespace"/> 的接口添加为控制器
        /// </summary>
        public void AddInterfacesAsControllers(string @namespace)
            => AddInterfacesAsControllers(AssemblyResolver.Default.GetTypesWithNamespace(@namespace, true));

        /// <summary>
        /// 将指定接口添加为控制器
        /// </summary>
        public void AddInterfacesAsControllers(IEnumerable<Type> interfaceTypes)
        {
            foreach (var type in interfaceTypes.Where(x => x.IsInterface))
            {
                var implementationType = AssemblyResolver.Default.GetTypesWithAssignableFrom(type).FirstOrDefault();
                if (implementationType is not null)
                {
                    _services.TryAddSingleton(type, implementationType);
                }
                _dynamicControllerTypes.Add(type);
            }
            InjectDynamicControllers();
        }

        private void InjectDynamicControllers()
        {
            _services.TryAddSingleton<DynamicControllersChangeProvider>();
            _services.TryAddSingleton<IActionDescriptorChangeProvider>(p => p.GetRequiredService<DynamicControllersChangeProvider>());
            AddConfigureHandler("DynamicControllers", app =>
            {
                if (_dynamicControllerTypes.Count > 0)
                {
                    app.ApplicationServices.GetRequiredService<DynamicControllersChangeProvider>().InterfacesAsControllers(_dynamicControllerTypes);
                    _dynamicControllerTypes.Clear();
                }
            });
        }

        private class DynamicControllersChangeProvider : IActionDescriptorChangeProvider
        {
            public DynamicControllersChangeProvider(ApplicationPartManager applicationPartManager, ILogger<WebApiOptions> logger)
            {
                _applicationPartManager = applicationPartManager;
                _logger = logger;
            }

            private CancellationTokenSource _tokenSource = new();
            private readonly ApplicationPartManager _applicationPartManager;
            private readonly ILogger<WebApiOptions> _logger;

            public bool HasChanged { get; private set; }

            public IChangeToken GetChangeToken() => new CancellationChangeToken(_tokenSource.Token);

            public void InterfacesAsControllers(List<Type> interfaceTypes)
            {
                var assembly = DynamicControllerGenerator.GeneratController(interfaceTypes);
                var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
                foreach (var applicationPart in partFactory.GetApplicationParts(assembly))
                {
                    _applicationPartManager.ApplicationParts.Add(applicationPart);
                }
                HasChanged = true;
                var source = _tokenSource;
                _tokenSource = new CancellationTokenSource();
                source.Cancel();
                if (_logger is null)
                {
                    return;
                }
                foreach (var type in assembly.GetTypes())
                {
                    var intfc = type.GetInterfaces().Where(x => interfaceTypes.Contains(x)).FirstOrDefault();
                    if (intfc is not null)
                    {
                        _logger.LogInformation($"    DynamicController -> {type.Name}: {intfc.FullName}");
                    }
                }
            }
        }



    }
}
